home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkSquare.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  17KB  |  587 lines

  1. /* 
  2.  * tkSquare.c --
  3.  *
  4.  *    This module implements "square" widgets.  A "square" is
  5.  *    a widget that displays a single square that can be moved
  6.  *    around and resized.  This file is intended as an example
  7.  *    of how to build a widget;  it isn't included in the
  8.  *    normal wish, but it is included in "tktest".
  9.  *
  10.  * Copyright (c) 1991-1994 The Regents of the University of California.
  11.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  12.  *
  13.  * See the file "license.terms" for information on usage and redistribution
  14.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15.  */
  16.  
  17. static char sccsid[] = "@(#) tkSquare.c 1.9 95/03/17 11:25:13";
  18.  
  19. #include "tkPort.h"
  20. #include "tk.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each square
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the square.  NULL
  29.                  * means window has been deleted but
  30.                  * widget record hasn't been cleaned up yet. */
  31.     Display *display;        /* X's token for the window's display. */
  32.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  33.     Tcl_Command widgetCmd;    /* Token for square's widget command. */
  34.     int x, y;            /* Position of square's upper-left corner
  35.                  * within widget. */
  36.     int size;            /* Width and height of square. */
  37.  
  38.     /*
  39.      * Information used when displaying widget:
  40.      */
  41.  
  42.     int borderWidth;        /* Width of 3-D border around whole widget. */
  43.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  44.     Tk_3DBorder fgBorder;    /* For drawing square. */
  45.     int relief;            /* Indicates whether window as a whole is
  46.                  * raised, sunken, or flat. */
  47.     GC gc;            /* Graphics context for copying from
  48.                  * off-screen pixmap onto screen. */
  49.     int doubleBuffer;        /* Non-zero means double-buffer redisplay
  50.                  * with pixmap;  zero means draw straight
  51.                  * onto the display. */
  52.     int updatePending;        /* Non-zero means a call to SquareDisplay
  53.                  * has already been scheduled. */
  54. } Square;
  55.  
  56. /*
  57.  * Information used for argv parsing.
  58.  */
  59.  
  60. static Tk_ConfigSpec configSpecs[] = {
  61.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  62.     "#cdb79e", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY},
  63.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  64.     "white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY},
  65.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  66.     (char *) NULL, 0, 0},
  67.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  68.     (char *) NULL, 0, 0},
  69.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  70.     "2", Tk_Offset(Square, borderWidth), 0},
  71.     {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
  72.     "1", Tk_Offset(Square, doubleBuffer), 0},
  73.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  74.     (char *) NULL, 0, 0},
  75.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  76.     "#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY},
  77.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  78.     "black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY},
  79.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  80.     "raised", Tk_Offset(Square, relief), 0},
  81.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  82.     (char *) NULL, 0, 0}
  83. };
  84.  
  85. /*
  86.  * Forward declarations for procedures defined later in this file:
  87.  */
  88.  
  89. static void        SquareCmdDeletedProc _ANSI_ARGS_((
  90.                 ClientData clientData));
  91. static int        SquareConfigure _ANSI_ARGS_((Tcl_Interp *interp,
  92.                 Square *squarePtr, int argc, char **argv,
  93.                 int flags));
  94. static void        SquareDestroy _ANSI_ARGS_((ClientData clientData));
  95. static void        SquareDisplay _ANSI_ARGS_((ClientData clientData));
  96. static void        KeepInWindow _ANSI_ARGS_((Square *squarePtr));
  97. static void        SquareEventProc _ANSI_ARGS_((ClientData clientData,
  98.                 XEvent *eventPtr));
  99. static int        SquareWidgetCmd _ANSI_ARGS_((ClientData clientData,
  100.                 Tcl_Interp *, int argc, char **argv));
  101.  
  102. /*
  103.  *--------------------------------------------------------------
  104.  *
  105.  * SquareCmd --
  106.  *
  107.  *    This procedure is invoked to process the "square" Tcl
  108.  *    command.  It creates a new "square" widget.
  109.  *
  110.  * Results:
  111.  *    A standard Tcl result.
  112.  *
  113.  * Side effects:
  114.  *    A new widget is created and configured.
  115.  *
  116.  *--------------------------------------------------------------
  117.  */
  118.  
  119. int
  120. SquareCmd(clientData, interp, argc, argv)
  121.     ClientData clientData;    /* Main window associated with
  122.                  * interpreter. */
  123.     Tcl_Interp *interp;        /* Current interpreter. */
  124.     int argc;            /* Number of arguments. */
  125.     char **argv;        /* Argument strings. */
  126. {
  127.     Tk_Window main = (Tk_Window) clientData;
  128.     Square *squarePtr;
  129.     Tk_Window tkwin;
  130.  
  131.     if (argc < 2) {
  132.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  133.         argv[0], " pathName ?options?\"", (char *) NULL);
  134.     return TCL_ERROR;
  135.     }
  136.  
  137.     tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
  138.     if (tkwin == NULL) {
  139.     return TCL_ERROR;
  140.     }
  141.     Tk_SetClass(tkwin, "Square");
  142.  
  143.     /*
  144.      * Allocate and initialize the widget record.
  145.      */
  146.  
  147.     squarePtr = (Square *) ckalloc(sizeof(Square));
  148.     squarePtr->tkwin = tkwin;
  149.     squarePtr->display = Tk_Display(tkwin);
  150.     squarePtr->interp = interp;
  151.     squarePtr->widgetCmd = Tcl_CreateCommand(interp,
  152.         Tk_PathName(squarePtr->tkwin), SquareWidgetCmd,
  153.         (ClientData) squarePtr, SquareCmdDeletedProc);
  154.     squarePtr->x = 0;
  155.     squarePtr->y = 0;
  156.     squarePtr->size = 20;
  157.     squarePtr->borderWidth = 0;
  158.     squarePtr->bgBorder = NULL;
  159.     squarePtr->fgBorder = NULL;
  160.     squarePtr->relief = TK_RELIEF_FLAT;
  161.     squarePtr->gc = None;
  162.     squarePtr->doubleBuffer = 1;
  163.     squarePtr->updatePending = 0;
  164.  
  165.     Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
  166.         SquareEventProc, (ClientData) squarePtr);
  167.     if (SquareConfigure(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) {
  168.     Tk_DestroyWindow(squarePtr->tkwin);
  169.     return TCL_ERROR;
  170.     }
  171.  
  172.     interp->result = Tk_PathName(squarePtr->tkwin);
  173.     return TCL_OK;
  174. }
  175.  
  176. /*
  177.  *--------------------------------------------------------------
  178.  *
  179.  * SquareWidgetCmd --
  180.  *
  181.  *    This procedure is invoked to process the Tcl command
  182.  *    that corresponds to a widget managed by this module.
  183.  *    See the user documentation for details on what it does.
  184.  *
  185.  * Results:
  186.  *    A standard Tcl result.
  187.  *
  188.  * Side effects:
  189.  *    See the user documentation.
  190.  *
  191.  *--------------------------------------------------------------
  192.  */
  193.  
  194. static int
  195. SquareWidgetCmd(clientData, interp, argc, argv)
  196.     ClientData clientData;        /* Information about square widget. */
  197.     Tcl_Interp *interp;            /* Current interpreter. */
  198.     int argc;                /* Number of arguments. */
  199.     char **argv;            /* Argument strings. */
  200. {
  201.     Square *squarePtr = (Square *) clientData;
  202.     int result = TCL_OK;
  203.     size_t length;
  204.     char c;
  205.  
  206.     if (argc < 2) {
  207.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  208.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  209.     return TCL_ERROR;
  210.     }
  211.     Tk_Preserve((ClientData) squarePtr);
  212.     c = argv[1][0];
  213.     length = strlen(argv[1]);
  214.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  215.         && (length >= 2)) {
  216.     if (argc != 3) {
  217.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  218.             argv[0], " cget option\"",
  219.             (char *) NULL);
  220.         goto error;
  221.     }
  222.     result = Tk_ConfigureValue(interp, squarePtr->tkwin, configSpecs,
  223.         (char *) squarePtr, argv[2], 0);
  224.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  225.         && (length >= 2)) {
  226.     if (argc == 2) {
  227.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  228.             (char *) squarePtr, (char *) NULL, 0);
  229.     } else if (argc == 3) {
  230.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  231.             (char *) squarePtr, argv[2], 0);
  232.     } else {
  233.         result = SquareConfigure(interp, squarePtr, argc-2, argv+2,
  234.             TK_CONFIG_ARGV_ONLY);
  235.     }
  236.     } else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) {
  237.     if ((argc != 2) && (argc != 4)) {
  238.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  239.             argv[0], " position ?x y?\"", (char *) NULL);
  240.         goto error;
  241.     }
  242.     if (argc == 4) {
  243.         if ((Tk_GetPixels(interp, squarePtr->tkwin, argv[2],
  244.             &squarePtr->x) != TCL_OK) || (Tk_GetPixels(interp,
  245.             squarePtr->tkwin, argv[3], &squarePtr->y) != TCL_OK)) {
  246.         goto error;
  247.         }
  248.         KeepInWindow(squarePtr);
  249.     }
  250.     sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y);
  251.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  252.     if ((argc != 2) && (argc != 3)) {
  253.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  254.             argv[0], " size ?amount?\"", (char *) NULL);
  255.         goto error;
  256.     }
  257.     if (argc == 3) {
  258.         int i;
  259.  
  260.         if (Tk_GetPixels(interp, squarePtr->tkwin, argv[2], &i) != TCL_OK) {
  261.         goto error;
  262.         }
  263.         if ((i <= 0) || (i > 100)) {
  264.         Tcl_AppendResult(interp, "bad size \"", argv[2],
  265.             "\"", (char *) NULL);
  266.         goto error;
  267.         }
  268.         squarePtr->size = i;
  269.         KeepInWindow(squarePtr);
  270.     }
  271.     sprintf(interp->result, "%d", squarePtr->size);
  272.     } else {
  273.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  274.         "\":  must be cget, configure, position, or size",
  275.         (char *) NULL);
  276.     goto error;
  277.     }
  278.     if (!squarePtr->updatePending) {
  279.     Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  280.     squarePtr->updatePending = 1;
  281.     }
  282.     Tk_Release((ClientData) squarePtr);
  283.     return result;
  284.  
  285.     error:
  286.     Tk_Release((ClientData) squarePtr);
  287.     return TCL_ERROR;
  288. }
  289.  
  290. /*
  291.  *----------------------------------------------------------------------
  292.  *
  293.  * SquareConfigure --
  294.  *
  295.  *    This procedure is called to process an argv/argc list in
  296.  *    conjunction with the Tk option database to configure (or
  297.  *    reconfigure) a square widget.
  298.  *
  299.  * Results:
  300.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  301.  *    returned, then interp->result contains an error message.
  302.  *
  303.  * Side effects:
  304.  *    Configuration information, such as colors, border width,
  305.  *    etc. get set for squarePtr;  old resources get freed,
  306.  *    if there were any.
  307.  *
  308.  *----------------------------------------------------------------------
  309.  */
  310.  
  311. static int
  312. SquareConfigure(interp, squarePtr, argc, argv, flags)
  313.     Tcl_Interp *interp;            /* Used for error reporting. */
  314.     Square *squarePtr;            /* Information about widget. */
  315.     int argc;                /* Number of valid entries in argv. */
  316.     char **argv;            /* Arguments. */
  317.     int flags;                /* Flags to pass to
  318.                      * Tk_ConfigureWidget. */
  319. {
  320.     if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs,
  321.         argc, argv, (char *) squarePtr, flags) != TCL_OK) {
  322.     return TCL_ERROR;
  323.     }
  324.  
  325.     /*
  326.      * Set the background for the window and create a graphics context
  327.      * for use during redisplay.
  328.      */
  329.  
  330.     Tk_SetWindowBackground(squarePtr->tkwin,
  331.         Tk_3DBorderColor(squarePtr->bgBorder)->pixel);
  332.     if ((squarePtr->gc == None) && (squarePtr->doubleBuffer)) {
  333.     XGCValues gcValues;
  334.     gcValues.function = GXcopy;
  335.     gcValues.graphics_exposures = False;
  336.     squarePtr->gc = Tk_GetGC(squarePtr->tkwin,
  337.         GCFunction|GCGraphicsExposures, &gcValues);
  338.     }
  339.  
  340.     /*
  341.      * Register the desired geometry for the window.  Then arrange for
  342.      * the window to be redisplayed.
  343.      */
  344.  
  345.     Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
  346.     Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth);
  347.     if (!squarePtr->updatePending) {
  348.     Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  349.     squarePtr->updatePending = 1;
  350.     }
  351.     return TCL_OK;
  352. }
  353.  
  354. /*
  355.  *--------------------------------------------------------------
  356.  *
  357.  * SquareEventProc --
  358.  *
  359.  *    This procedure is invoked by the Tk dispatcher for various
  360.  *    events on squares.
  361.  *
  362.  * Results:
  363.  *    None.
  364.  *
  365.  * Side effects:
  366.  *    When the window gets deleted, internal structures get
  367.  *    cleaned up.  When it gets exposed, it is redisplayed.
  368.  *
  369.  *--------------------------------------------------------------
  370.  */
  371.  
  372. static void
  373. SquareEventProc(clientData, eventPtr)
  374.     ClientData clientData;    /* Information about window. */
  375.     XEvent *eventPtr;        /* Information about event. */
  376. {
  377.     Square *squarePtr = (Square *) clientData;
  378.  
  379.     if (eventPtr->type == Expose) {
  380.     if (!squarePtr->updatePending) {
  381.         Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  382.         squarePtr->updatePending = 1;
  383.     }
  384.     } else if (eventPtr->type == ConfigureNotify) {
  385.     KeepInWindow(squarePtr);
  386.     if (!squarePtr->updatePending) {
  387.         Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  388.         squarePtr->updatePending = 1;
  389.     }
  390.     } else if (eventPtr->type == DestroyNotify) {
  391.     if (squarePtr->tkwin != NULL) {
  392.         squarePtr->tkwin = NULL;
  393.         Tcl_DeleteCommand(squarePtr->interp,
  394.             Tcl_GetCommandName(squarePtr->interp,
  395.             squarePtr->widgetCmd));
  396.     }
  397.     if (squarePtr->updatePending) {
  398.         Tk_CancelIdleCall(SquareDisplay, (ClientData) squarePtr);
  399.     }
  400.     Tk_EventuallyFree((ClientData) squarePtr, SquareDestroy);
  401.     }
  402. }
  403.  
  404. /*
  405.  *----------------------------------------------------------------------
  406.  *
  407.  * SquareCmdDeletedProc --
  408.  *
  409.  *    This procedure is invoked when a widget command is deleted.  If
  410.  *    the widget isn't already in the process of being destroyed,
  411.  *    this command destroys it.
  412.  *
  413.  * Results:
  414.  *    None.
  415.  *
  416.  * Side effects:
  417.  *    The widget is destroyed.
  418.  *
  419.  *----------------------------------------------------------------------
  420.  */
  421.  
  422. static void
  423. SquareCmdDeletedProc(clientData)
  424.     ClientData clientData;    /* Pointer to widget record for widget. */
  425. {
  426.     Square *squarePtr = (Square *) clientData;
  427.     Tk_Window tkwin = squarePtr->tkwin;
  428.  
  429.     /*
  430.      * This procedure could be invoked either because the window was
  431.      * destroyed and the command was then deleted (in which case tkwin
  432.      * is NULL) or because the command was deleted, and then this procedure
  433.      * destroys the widget.
  434.      */
  435.  
  436.     if (tkwin != NULL) {
  437.     squarePtr->tkwin = NULL;
  438.     Tk_DestroyWindow(tkwin);
  439.     }
  440. }
  441.  
  442. /*
  443.  *--------------------------------------------------------------
  444.  *
  445.  * SquareDisplay --
  446.  *
  447.  *    This procedure redraws the contents of a square window.
  448.  *    It is invoked as a do-when-idle handler, so it only runs
  449.  *    when there's nothing else for the application to do.
  450.  *
  451.  * Results:
  452.  *    None.
  453.  *
  454.  * Side effects:
  455.  *    Information appears on the screen.
  456.  *
  457.  *--------------------------------------------------------------
  458.  */
  459.  
  460. static void
  461. SquareDisplay(clientData)
  462.     ClientData clientData;    /* Information about window. */
  463. {
  464.     Square *squarePtr = (Square *) clientData;
  465.     Tk_Window tkwin = squarePtr->tkwin;
  466.     Pixmap pm = None;
  467.     Drawable d;
  468.  
  469.     squarePtr->updatePending = 0;
  470.     if (!Tk_IsMapped(tkwin)) {
  471.     return;
  472.     }
  473.  
  474.     /*
  475.      * Create a pixmap for double-buffering, if necessary.
  476.      */
  477.  
  478.     if (squarePtr->doubleBuffer) {
  479.     pm = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  480.         (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
  481.         (unsigned) DefaultDepthOfScreen(Tk_Screen(tkwin)));
  482.     d = pm;
  483.     } else {
  484.     d = Tk_WindowId(tkwin);
  485.     }
  486.  
  487.     /*
  488.      * Redraw the widget's background and border.
  489.      */
  490.  
  491.     Tk_Fill3DRectangle(tkwin, d, squarePtr->bgBorder, 0, 0, Tk_Width(tkwin),
  492.         Tk_Height(tkwin), squarePtr->borderWidth, squarePtr->relief);
  493.  
  494.     /*
  495.      * Display the square.
  496.      */
  497.  
  498.     Tk_Fill3DRectangle(tkwin, d, squarePtr->fgBorder, squarePtr->x,
  499.         squarePtr->y, squarePtr->size, squarePtr->size,
  500.         squarePtr->borderWidth, TK_RELIEF_RAISED);
  501.  
  502.     /*
  503.      * If double-buffered, copy to the screen and release the pixmap.
  504.      */
  505.  
  506.     if (squarePtr->doubleBuffer) {
  507.     XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), squarePtr->gc,
  508.         0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
  509.         0, 0);
  510.     XFreePixmap(Tk_Display(tkwin), pm);
  511.     }
  512. }
  513.  
  514. /*
  515.  *----------------------------------------------------------------------
  516.  *
  517.  * SquareDestroy --
  518.  *
  519.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  520.  *    to clean up the internal structure of a square at a safe time
  521.  *    (when no-one is using it anymore).
  522.  *
  523.  * Results:
  524.  *    None.
  525.  *
  526.  * Side effects:
  527.  *    Everything associated with the square is freed up.
  528.  *
  529.  *----------------------------------------------------------------------
  530.  */
  531.  
  532. static void
  533. SquareDestroy(clientData)
  534.     ClientData clientData;    /* Info about square widget. */
  535. {
  536.     Square *squarePtr = (Square *) clientData;
  537.  
  538.     Tk_FreeOptions(configSpecs, (char *) squarePtr, squarePtr->display, 0);
  539.     if (squarePtr->gc != None) {
  540.     Tk_FreeGC(squarePtr->display, squarePtr->gc);
  541.     }
  542.     ckfree((char *) squarePtr);
  543. }
  544.  
  545. /*
  546.  *----------------------------------------------------------------------
  547.  *
  548.  * KeepInWindow --
  549.  *
  550.  *    Adjust the position of the square if necessary to keep it in
  551.  *    the widget's window.
  552.  *
  553.  * Results:
  554.  *    None.
  555.  *
  556.  * Side effects:
  557.  *    The x and y position of the square are adjusted if necessary
  558.  *    to keep the square in the window.
  559.  *
  560.  *----------------------------------------------------------------------
  561.  */
  562.  
  563. static void
  564. KeepInWindow(squarePtr)
  565.     register Square *squarePtr;        /* Pointer to widget record. */
  566. {
  567.     int i, bd;
  568.     bd = 0;
  569.     if (squarePtr->relief != TK_RELIEF_FLAT) {
  570.     bd = squarePtr->borderWidth;
  571.     }
  572.     i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size);
  573.     if (i < 0) {
  574.     squarePtr->x += i;
  575.     }
  576.     i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size);
  577.     if (i < 0) {
  578.     squarePtr->y += i;
  579.     }
  580.     if (squarePtr->x < bd) {
  581.     squarePtr->x = bd;
  582.     }
  583.     if (squarePtr->y < bd) {
  584.     squarePtr->y = bd;
  585.     }
  586. }
  587.